#include "Globals.h"
#include "imesh.h"

class CGame;
//--------------------------------------------------------------------------------
//	initialize a mesh asset
//--------------------------------------------------------------------------------
void *IMesh::AssetInit( IAssetFile *pi, const CMasterIndexEntry *pme, void *context )
{
	CGraphics	*pGraphics = (CGraphics*)context;
	IMesh		*ret;
	void		*pchunk;
	u32			length;
	u32			i;

	ret = (IMesh*)pi->GetChunk(pme->nameHash,pme->primaryChunk);
	if( ret )
	{
		// restore the pointers that are stored in this chunk
		ret->pBones     = OffsetToPointer(ret->pBones,ret);
		ret->pRefPoints = OffsetToPointer(ret->pRefPoints,ret);
		ret->pMaterials = OffsetToPointer(ret->pMaterials,ret);
		ret->pStrips    = OffsetToPointer(ret->pStrips,ret);
		ret->vertexDecl = OffsetToPointer(ret->vertexDecl,ret);
 
		// create the vertex format
		ret->pVFormat = pGraphics->CreateVertexFormat(ret->vertexDecl);

		// get the index chunk
		// create the index buffer
		pchunk = pi->GetChunk(pme->nameHash,*(LOCID*)&ret->pIndices);
		ret->pIndices = pGraphics->CreateIndexBuffer(ret->numIndices,(u16*)pchunk,true);

		for( i=0;i<ret->numStreams;i++ )
		{
			// get the vertex chunk
			pchunk = pi->GetChunk(pme->nameHash,*(LOCID*)&ret->pVertices[i],&length);
			ret->pVertices[i] = pGraphics->CreateVertexBuffer(length,pchunk,true);
		}

		void* mem = pi->GetChunk( pme->nameHash, *(LOCID*)&ret->pPhysicsCollection, &length );
		ret->pPhysicsCollection = NXU::loadCollection( "Physics", (NXU::NXU_FileType)ret->physicsFmt, mem, length );
	}
	return (void*)ret;
}

//--------------------------------------------------------------------------------
//	uninitialize a mesh asset
//--------------------------------------------------------------------------------
void IMesh::AssetUninit( void *ptr, void *context )
{
	IMesh	*pMesh = (IMesh *)ptr;
	u32		i;

	if( pMesh->pIndices )
	{
		pMesh->pIndices->Release();
		pMesh->pIndices = 0;
	}
	if( pMesh->pVFormat )
	{
		pMesh->pVFormat->Release();
		pMesh->pVFormat = 0;
	}
	for( i=0;i<pMesh->numStreams;i++ )
	{
		if( pMesh->pVertices[i] )
		{
			pMesh->pVertices[i]->Release();
			pMesh->pVertices[i] = 0;
		}
	}
}

//--------------------------------------------------------------------------------
//	fixup an asset
//--------------------------------------------------------------------------------
void IMesh::AssetFixup( CAssetFileMgr *pMgr, const IAssetFile *pFile, void *ptr, const CMasterIndexEntry *pEntry )
{
	IMesh		*pMesh = (IMesh*)ptr;
	
	pMesh->pVShader = (IVertexShader*)pMgr->FindAsset( (HASH)pMesh->pVShader, "VSHADER" );

	GetGraphics()->FixupMaterial( pMgr, pMesh->pMaterials, pMesh->numMaterials );

	GetPhysics()->AddCollection( pMesh->pPhysicsCollection );
}

//--------------------------------------------------------------------------------
//	apply an animation to a mesh
//--------------------------------------------------------------------------------
void IMesh::ApplyAnimation( const CAnimResult *result, CTransMat *outMats ) const
{
	u32				i;
	SMeshBoneData	*pb;
	CVector			v;
	CQuaternion			q,tq;

	if( result )
	{
		// make matrices from the quatList
		for( i=0,pb=pBones;i<numBones;i++,pb++ )
		{
			// uncompress the bone's binding quat
			q.UnCompress(pb->invRotation);
	
			// multiply the binding quat by the animation quat
			tq.Multiply(q,result->quatList[i]);
			
			// create the output matrix
			// orientation
			tq.toTransMat(outMats[i]);
	
			// offset from parent bone if applicable
			if( pb->parentBone!=i )
			{
				outMats[i].SetPos(outMats[pb->parentBone],pb->worldOffset);
			}
			else
			{
				v.Sum(pb->pivot,result->rootPos);
				outMats[i].SetPos(v);
			}
		}
	
		// correct for pivot point
		for( i=0,pb=pBones;i<numBones;i++,pb++ )
			outMats[i].SetPivot( pb->pivot );
	}
	else
	{
		for( i=0,pb=pBones;i<numBones;i++,pb++ )
			outMats[i].Identity();
	}
}

//--------------------------------------------------------------------------------------------
//	get the matrix of a bone
//--------------------------------------------------------------------------------------------
CMatrix &IMesh::GetBoneMatrix( const CAnimResult &animResult, u32 idx, CMatrix &result ) const
{
	if( pBones[idx].parentBone!=idx )
	{
		GetBoneMatrix(animResult,pBones[idx].parentBone,result);
		result.Mult(pBones[idx].localOffset,result[3]);
	}
	else
		result[3].Sum(pBones[idx].pivot,animResult.rootPos);

	animResult.quatList[idx].toMatrix(result);
	return result;
}


//--------------------------------------------------------------------------------------------
//	get a reference point
//--------------------------------------------------------------------------------------------
CMatrix &IMesh::GetRefMatrix( const CAnimResult &animResult, u32 idx, CMatrix &result ) const
{
	CQuaternion			q,r;
	SMeshRefPntData	*pref;
	u32				pidx;

	pref = &pRefPoints[idx];
	pidx = pref->parentBone;

	GetBoneMatrix(animResult,pidx,result);
	result.Mult(pref->localOffset,result[3]);

	r.UnCompress(pref->rotOffset);
	q.Multiply(r,animResult.quatList[pidx]);
	q.toMatrix(result);
	return result;
}

//--------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------
bool IMesh::FindRefPoint( const char *name, u32 &pIndex ) const
{
	HASH	nameHash = MakeStringHash(name);
	u32		idx;

	for( idx=0;idx<numRefPoints;idx++ )
	{
		if( pRefPoints[idx].nameHash==nameHash )
		{
			pIndex = idx;
			return true;
		}
	}
	return false;
}